/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package xenon3d.scene;

import javax.media.opengl.GL2;

/**
 * The RenderingAttributes class defines common rendering attributes for all
 * primitive types.
 * 
 * @author Volker Everts
 * @version 0.1 - 18.11.2011: Created
 */
public class RenderingAttributes extends Attributes {
    
    // <editor-fold defaultstate="collapsed" desc=" Static Attributes ">
    
    /** The default rendering attributes. */
    static final RenderingAttributes DEFAULT = new RenderingAttributes();

    /** The last active rendering attributes object. */
    private static RenderingAttributes last = DEFAULT;
    
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc=" Public Enumerations ">
    
    /**
     * An enumeration of all test values for alpha and depth testing.
     */
    public static enum TestFunction {
        
        /** Never passes. */
        Never (GL2.GL_NEVER),
        
        /** Passes if the incoming value is less than the reference value. */
        Less (GL2.GL_LESS),
        
        /** Passes if the incoming value is less than or equal to the reference value. */
        LessEqual (GL2.GL_LEQUAL),
        
        /** Passes if the incoming value is equal to the reference value. */
        Equal (GL2.GL_EQUAL),
        
        /** Passes if the incoming value is greater than or equal to the reference value. */
        GreaterEqual (GL2.GL_GEQUAL),
        
        /** Passes if the incoming value is greater than the reference value. */
        Greater (GL2.GL_GREATER),
        
        /** Passes if the incoming value is not equal to the reference value. */
        NotEqual (GL2.GL_NOTEQUAL),
        
        /** Always passes. */
        Always (GL2.GL_ALWAYS);
        
        /** The internal OpenGL test value. */
        int value;
        
        /**
         * Creates a new TestFunction.
         * @param value the OpenGL test value
         */
        TestFunction(int value) {
            this.value = value;
        }
        
    } // end enum TestFunction
    
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc=" Private Fields ">
    
    /** The alpha test function. */
    private TestFunction alphaFunc = TestFunction.Always;
    
    /** The depth test function. */
    private TestFunction depthFunc = TestFunction.Less;
    
    /** The alpha test value. */
    private float alphaValue = 0.0f;
    
    /** The alpha test flag. */
    private boolean alphaTest = false;
    
    /** The depth test flag. */
    private boolean depthTest = true;
    
    /** The depth buffer write flag. */
    private boolean depthBufferWrite = true;
    
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc=" Initialization ">
    
    /**
     * Creates a new RenderingAttributes object using default parameter
     * settings. The default settings are:<p>
     * <ul>
     * </ul>
     */
    public RenderingAttributes() {}
    
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc=" Public Properties ">
    
    /**
     * Enables or disables alpha testing.
     * @param enable if true, alpha testing will be enabled
     */
    public final void setAlphaTest(boolean enable) {
        alphaTest = enable;
    }
    
    /**
     * Returns whether or not alpha testing is enabled.
     * @return true, if alpha testing is enabled
     */
    public final boolean isAlphaTest() {
        return alphaTest;
    }
    
    /**
     * Sets the alpha test function.
     * @param test the new alpha test function
     */
    public final void setAlphaTestFunction(TestFunction test) {
        if (test == null) throw new IllegalArgumentException();
        alphaFunc = test;
    }
    
    /**
     * Returns the alpha test function.
     * @return the current alpha test function
     */
    public final TestFunction getAlphaTestFunction() {
        return alphaFunc;
    }
    
    /**
     * Sets the alpha test value used by alpha test function. This value is
     * clamped to the range 0 through 1, where 0 represents the lowest possible
     * alpha value and 1 the highest possible value.
     * @param value the new alpha test value
     */
    public final void setAlphaTestValue(float value) {
        alphaValue = value;
    }
    
    /**
     * Returns the alpha test value.
     * @return the current alpha test value
     */
    public final float getAlphaTestValue() {
        return alphaValue;
    }
    
    /**
     * Enables or disables the depth buffer.
     * @param enable if true, the depth buffer will be enabled
     */
    public final void setDepthTest(boolean enable) {
        depthTest = enable;
    }
    
    /**
     * Returns whether or not depth buffering is enabled.
     * @return true, if depth buffering is enabled
     */
    public final boolean isDepthTest() {
        return depthTest;
    }
    
    /**
     * Sets the depth test function.
     * @param test the new depth test function
     */
    public final void setDepthTestFunction(TestFunction test) {
        depthFunc = test;
    }
    
    /**
     * Returns the depth test function.
     * @return the current depth test function
     */
    public final TestFunction getDepthTestFunction() {
        return depthFunc;
    }
    
    /**
     * Enables or disables writing to the depth buffer.
     * @param enable if true, depth buffer write will be enabled
     */
    public final void setDepthBufferWrite(boolean enable) {
        depthBufferWrite = enable;
    }
    
    /**
     * Returns whether or not depth buffer writing is enabled.
     * @return true, if depth buffer write is enabled
     */
    public final boolean isDepthBufferWrite() {
        return depthBufferWrite;
    }
    
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc=" Package Private Methods (OpenGL) ">
    
    @Override
    void apply(GL2 gl) {
        if (this == last) return;
        // Alpha-Blending
        if (alphaTest != last.alphaTest) {
            if (alphaTest) {
                gl.glEnable(GL2.GL_ALPHA_TEST);
                gl.glAlphaFunc(alphaFunc.value, alphaValue);
            }
            else gl.glDisable(GL2.GL_ALPHA_TEST);
        }
        // Depth-Testing
        if (depthTest != last.depthTest) {
            if (depthTest) {
                gl.glEnable(GL2.GL_DEPTH_TEST);
                gl.glDepthFunc(depthFunc.value);
                gl.glDepthMask(depthBufferWrite);
            }
            else gl.glDisable(GL2.GL_DEPTH_TEST);
        }
        last = this;
    }

    // </editor-fold>
        
} // end class RenderingAttributes